﻿using System;
using System.Collections.Generic;
using gbL.NetBoard.Broadcast;
using NUnit.Framework;

namespace gbL.NetBoard.Tests
{
    [TestFixture]
    public class BroadcastTests
    {
        [SetUp]
        public void Prior_to_each_test()
        {
            _actual = new List<object>();
        }

        private List<object> _actual;

        private void OnChanged(object sender, ChangedEventArgs e)
        {
            _actual.Add(e.Entry);
        }

        [Test]
        public void Should_be_able_to_limit_Added_broadcasts_by_func()
        {
            // A
            Broadcaster target = new Broadcaster();
            BlackboardEntry message1 = new BlackboardEntry(null, null, 42);
            BlackboardEntry message2 = new BlackboardEntry(null, null, 41);
            target.Added +=new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterAddedByFunc(this, x => x.Message is int && ((int)x.Message == 42));
            // A
            target.NotifyAdded(null, message1);
            target.NotifyAdded(null, message2);
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(message1));
        }

        [Test]
        public void Should_be_able_to_limit_Removed_broadcasts_by_func()
        {
            // A
            Broadcaster target = new Broadcaster();
            BlackboardEntry message1 = new BlackboardEntry(null, null, "hello");
            BlackboardEntry message2 = new BlackboardEntry(null, null, "goodbye");
            target.Removed += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterRemovedByFunc(this, x => x.Message!=null && x.Message.ToString().Equals("goodbye"));
            // A
            target.NotifyRemoved(null, new IBlackboardEntry[] { message1 });
            target.NotifyRemoved(null, new IBlackboardEntry[] { message2 });
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(message2));
        }

        [Test]
        public void Should_be_able_to_limit_Added_broadcasts_by_tag()
        {
            // A
            Broadcaster target = new Broadcaster();
            object tag1 = new object();
            object tag2 = new object();
            BlackboardEntry message1 = new BlackboardEntry(tag1, null, null);
            BlackboardEntry message2 = new BlackboardEntry(tag2, null, null);
            target.Added += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterAddedByTag(this, tag2);
            // A
            target.NotifyAdded(null, message1);
            target.NotifyAdded(null, message2);
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(message2));
        }

        [Test]
        public void Should_be_able_to_limit_Removed_broadcasts_by_tag()
        {
            // A
            Broadcaster target = new Broadcaster();
            object tag1 = new object();
            object tag2 = new object();
            BlackboardEntry message1 = new BlackboardEntry(tag1, null, null);
            BlackboardEntry message2 = new BlackboardEntry(tag2, null, null);
            target.Removed += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterRemovedByTag(this, tag1);
            // A
            target.NotifyRemoved(null, new IBlackboardEntry[] { message1 });
            target.NotifyRemoved(null, new IBlackboardEntry[] { message2 });
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(message1));
        }

        [Test]
        public void Should_be_able_to_limit_Added_broadcasts_by_message_type()
        {
            // A
            Broadcaster target = new Broadcaster();
            BlackboardEntry stringMessage = new BlackboardEntry(null, null, "hello");
            BlackboardEntry intMessage = new BlackboardEntry(null, null, 4);
            target.Added += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterAddedByType(this, typeof(string));
            // A
            target.NotifyAdded(null, stringMessage);
            target.NotifyAdded(null, intMessage);
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(stringMessage));
        }

        [Test]
        public void Should_be_able_to_limit_Removed_broadcasts_by_message_type()
        {
            // A
            Broadcaster target = new Broadcaster();
            BlackboardEntry stringMessage = new BlackboardEntry(null, null, "hello");
            BlackboardEntry intMessage = new BlackboardEntry(null, null, 4);
            target.Removed += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterRemovedByType(this, typeof(int));
            // A
            target.NotifyRemoved(null, new IBlackboardEntry[] { stringMessage });
            target.NotifyRemoved(null, new IBlackboardEntry[] { intMessage });
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(intMessage));
        }

        [Test]
        public void Should_be_able_to_limit_broadcasts_by_message_supertype()
        {
            // A
            Broadcaster target = new Broadcaster();

            BlackboardEntry greetingMessage = new BlackboardEntry(
                null, null, new ASubType() { Greeting = "hello" }
            );
            BlackboardEntry intMessage = new BlackboardEntry(null, null, 4);

            target.Added += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterAddedByType(this, typeof(ABaseType));

            // A
            target.NotifyAdded(null, greetingMessage);
            target.NotifyAdded(null, intMessage);

            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(greetingMessage));
        }

        [Test]
        public void Should_be_able_to_limit_broadcasts_by_message_interface()
        {
            // A
            Broadcaster target = new Broadcaster();

            BlackboardEntry greetingMessage = new BlackboardEntry(
                null, null, new ASubType() { Greeting = "hello" }
            );

            BlackboardEntry intMessage = new BlackboardEntry(null, null, 4);

            target.Added += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterAddedByType(this, typeof(ASpecialInterface));

            // A
            target.NotifyAdded(null, greetingMessage);
            target.NotifyAdded(null, intMessage);

            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(greetingMessage));
        }

        [Test]
        public void Should_be_able_to_limit_Added_broadcasts_by_from()
        {
            // A
            Broadcaster target = new Broadcaster();
            object from1 = "you";
            object from2 = "me";
            IBlackboardEntry message1 = new BlackboardEntry(null, from1, null);
            IBlackboardEntry message2 = new BlackboardEntry(null, from2, null);
            target.Added+=new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterAddedByFrom(this, from1);
            // A
            target.NotifyAdded(null, message1);
            target.NotifyAdded(null, message2);
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(message1));

        }

        [Test]
        public void Should_be_able_to_limit_Removed_broadcasts_by_from()
        {
            // A
            Broadcaster target = new Broadcaster();
            object from1 = "you";
            object from2 = "me";
            IBlackboardEntry message1 = new BlackboardEntry(null, from1, null);
            IBlackboardEntry message2 = new BlackboardEntry(null, from2, null);
            target.Removed += new EventHandler<ChangedEventArgs>(OnChanged);
            target.FilterRemovedByFrom(this, from1);
            // A
            target.NotifyRemoved(null, new IBlackboardEntry[] { message1 });
            target.NotifyRemoved(null, new IBlackboardEntry[] { message2 });
            // A
            Assert.That(_actual, Has.Count.EqualTo(1));
            Assert.That(_actual[0], Is.SameAs(message1));

        }

        interface ASpecialInterface { }

        private abstract class ABaseType { public string Greeting { get; set; } }

        private class ASubType : ABaseType, ASpecialInterface { }
    }
}
